分类
联系方式
  1. 新浪微博
  2. E-mail

Flutter Engine MessageLoop

介绍

该类来自于 Google 的跨平台基础库 fml。

MessageLoop 注释:与线程关联的事件循环。

MessageLoop 整个类继承体系由多个类组成,MessageLoop 是基类,还有一系列实现类。

MessageLoop

位于 fml/message_loop.h。

Thread Local

MessageLoop 是通过 Thread Local 存储在线程中的,对应于变量:

FML_THREAD_LOCAL ThreadLocalUniquePtr<MessageLoop> tls_message_loop;

通过 Message::EnsureInitializedForCurrentThread 初始化:

void MessageLoop::EnsureInitializedForCurrentThread() {
  if (tls_message_loop.get() != nullptr) {
    // Already initialized.
    return;
  }
  tls_message_loop.reset(new MessageLoop());
}

构造

MessageLoop 构造方法如下:

MessageLoop::MessageLoop()
    : loop_(MessageLoopImpl::Create()),
      task_runner_(fml::MakeRefCounted<fml::TaskRunner>(loop_)) {
  FML_CHECK(loop_);
  FML_CHECK(task_runner_);
}

构造方法中,首先创建了一个 MessageLoop 实现,同事页创建了 TaskRunner。

MessageLoopImpl

跨平台实现

根据不同平台导入不同头文件:

#if OS_MACOSX
#include "flutter/fml/platform/darwin/message_loop_darwin.h"
#elif OS_ANDROID
#include "flutter/fml/platform/android/message_loop_android.h"
#elif OS_FUCHSIA
#include "flutter/fml/platform/fuchsia/message_loop_fuchsia.h"
#elif OS_LINUX
#include "flutter/fml/platform/linux/message_loop_linux.h"
#elif OS_WIN
#include "flutter/fml/platform/win/message_loop_win.h"
#endif

Run

启动线程队列:

// message_loop.cc
void MessageLoop::Run() {
    loop_->DoRun();
}

// message_loop_impl.cc
void MessageLoopImpl::DoRun() {
  if (terminated_) {
    // Message loops 只能启动一次
    return;
  }

  // 该方法由平台实现来实现
  Run();

  // loop 可能会被停止。通过手动设置该标志实现。
  terminated_ = true;

  // 消息队列正在终止。检查是否由过期任务,这是过期任务的最后机会。
  RunExpiredTasksNow();

  // 当消息队列在终止过程中,后续任务需要从消息队列的线程中销毁
  task_queue_->DisposeTasks(queue_id_);
}

// 以 Android 为例 message_loop_android
void MessageLoopAndroid::Run() {
  running_ = true;
  // 死循环
  while (running_) {
    // NDK 方法
    int result = ::ALooper_pollOnce(-1, // infinite timeout
                                    nullptr, // out fd,
                                    nullptr, // out events,
                                    nullptr // out data
    );
    if (result == ALOOPER_POLL_TIMEOUT || result == ALOOPER_POLL_ERROR) {
      // This handles the case where the loop is terminated using ALooper APIs.
      running_ = false;
    }
  }
}

在 Android 实现中,可以看到是基于 Android 的 Loop 来实现消息队列的。

问题:ALooper_pollOnce 阻塞住了,消息是在哪里处理的?

消息响应

以 Android 为例,消息响应是在构造函数中注册的:

MessageLoopAndroid::MessageLoopAndroid()
    : looper_(AcquireLooperForThread()),
      timer_fd_(::timerfd_create(kClockType, TFD_NONBLOCK | TFD_CLOEXEC)),
      running_(false) {
  FML_CHECK(looper_.is_valid());
  FML_CHECK(timer_fd_.is_valid());

  static const int kWakeEvents = ALOOPER_EVENT_INPUT;

  ALooper_callbackFunc read_event_fd = [](int, int events, void* data) -> int {
    if (events & kWakeEvents) {
      reinterpret_cast<MessageLoopAndroid*>(data)->OnEventFired();
    }
    return 1; // continue receiving callbacks
  };

  int add_result = ::ALooper_addFd(looper_.get(), // looper
                                   timer_fd_.get(), // fd
                                   ALOOPER_POLL_CALLBACK, // ident
                                   kWakeEvents, // events
                                   read_event_fd, // callback
                                   this // baton
  );
  FML_CHECK(add_result == 1);
}

可以看到,消息处理函数是:

void MessageLoopAndroid::OnEventFired() {
  if (TimerDrain(timer_fd_.get())) {
    RunExpiredTasksNow();
  }
}

RunExpiredTasksNow

从具体实现回到基类,所有消息处理方法都是在 RunExpiredTasksNow 中实现的:

void MessageLoopImpl::RunExpiredTasksNow() {
  FlushTasks(FlushType::kAll);
}

FlushTasks:

void MessageLoopImpl::FlushTasks(FlushType type) {
  TRACE_EVENT0("fml", "MessageLoop::FlushTasks");

  const auto now = fml::TimePoint::Now();
  fml::closure invocation;
  do {
    invocation = task_queue_->GetNextTaskToRun(queue_id_, now);
    if (!invocation) {
      break;
    }
    invocation();
    std::vector<fml::closure> observers =
        task_queue_->GetObserversToNotify(queue_id_);
    for (const auto& observer : observers) {
      observer();
    }
    if (type == FlushType::kSingle) {
      break;
    }
  } while (invocation);
}

其中:

  • 从消息队列中取出的是闭包,取出后进行运行。
  • 消息队列还有观察者,每处理一个消息都调一次观察者
  • 如果模式是 kAll,会把消息队列全清掉

Flutter vs DartVM 消息队列

通过梳理,我发现 Flutter 的消息队列跟 DartVM Isolate 的消息队列不是一个东西。

  • Flutter 的消息队列是一个持久运行的消息队列,在 Android 基于 Looper
  • DartVM Isolate 是另一套消息队列,这套队列不是常驻的,按需运行在线程池提供的某一条线程上,用完后自动出让线程